﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace SunXNA.RainbowXNA
{
    partial class CRainbowXNA
    {

        public class CRenderInstance
        {
            protected static UInt32 s_LastUID = 0;

            protected CRainbowXNA.RenderObjectHandleType m_objectHandle;
            protected Matrix m_m4World;
            protected UInt32 m_UID;
            protected OBB m_obb;
            protected float m_rTimeStamp;
            protected Material.CBaseMaterial m_material;

            public Material.CBaseMaterial Material
            {
                get
                {
                    return m_material;
                }

                set
                {
                    m_material = value;
                }
            }

            public float TimeStamp
            {
                get
                {
                    return m_rTimeStamp;
                }

                set
                {
                    m_rTimeStamp = value;
                }
            }

            public CRainbowXNA.RenderObjectHandleType ObjectHandle
            {
                get
                {
                    return m_objectHandle;
                }

                set
                {
                    m_objectHandle = value;

                    if (m_objectHandle != null)
                    {
                        m_obb = OBB.CreateFromBox(m_objectHandle.GraphicsBox);
                    }
                    else
                    {
                        m_obb = null;
                    }
                }
            }

            public Matrix World
            {
                get
                {
                    return m_m4World;
                }

                set
                {
                    m_m4World = value;
                }
            }

            public BoundingSphere GraphicsVolume
            {
                get
                {
                    BoundingSphere volume = m_objectHandle.GraphicsVolume;
                    volume.Center = Vector3.Transform(volume.Center, m_m4World);
                    return volume;
                }
            }

            public BoundingBox GraphicsBox
            {
                get
                {
                    return (m_obb.Transform(ref m_m4World)).ToBoundingBox();
                }
            }

            public CRenderInstance()
            {
                ObjectHandle = null;
                World = Matrix.Identity;
                m_UID = ++s_LastUID;
                m_rTimeStamp = -1.0f;
                m_material = new SunXNA.RainbowXNA.Material.CFlatShadedMaterial();
            }

            public CRenderInstance(CRainbowXNA.RenderObjectHandleType objectHandle)
            {
                ObjectHandle = objectHandle;
                World = Matrix.Identity;

                m_UID = ++s_LastUID;

                m_rTimeStamp = -1.0f;
                m_material = new SunXNA.RainbowXNA.Material.CFlatShadedMaterial();

                if (m_objectHandle != null)
                {
                    m_obb = OBB.CreateFromBox(m_objectHandle.GraphicsBox);
                }
                else
                {
                    m_obb = null;
                }
            }

            /// <summary>
            /// Clears space in the render quad tree if this object should not be draw this frame
            /// </summary>
            public void Kill( )
            {
            }

            /// <summary>
            /// I'd rather you didnt use this. Having a Matrix in the user class has added values than just rendering.
            /// </summary>
            /// <param name="v3Position">3D world position of the instance</param>
            /// <param name="v3Scaling">3D scaling of the instance [I would definately prefer you didn't use this, it screws with sensible debuging values. Send in Vector3.One please]</param>
            /// <param name="qOrientation">3D world orientation</param>
            public void CreateWorld(Vector3 v3Position, Vector3 v3Scaling, Quaternion qOrientation)
            {
                Matrix m4Scale = Matrix.CreateScale(v3Scaling);
                Matrix m4Rotate = Matrix.CreateFromQuaternion(qOrientation);
                Matrix m4Translate = Matrix.CreateTranslation(v3Position);

                World = m4Scale * m4Rotate * m4Translate;
            }

            public void CreateWorld(Vector3 v3Position, Vector3 v3Heading, Vector3 v3Scaling )
            {
                Matrix m4Scale = Matrix.CreateScale(v3Scaling);
                Matrix m4World;
                if (Math.Abs( Vector3.Dot(v3Heading, Vector3.Up) ) < 0.98f)
                {
                    //m4Rotate = Matrix.CreateLookAt(Vector3.Zero, v3Heading, Vector3.Up);
                    m4World = Matrix.CreateWorld(v3Position, v3Heading, Vector3.Up);
                }
                else
                {
                    //m4Rotate = Matrix.CreateLookAt(Vector3.Zero, v3Heading, Vector3.Forward);
                    m4World = Matrix.CreateWorld(v3Position, v3Heading, Vector3.Forward);
                }

                World = m4Scale * m4World;
            }

            public static bool operator == (CRenderInstance lhs, CRenderInstance rhs )
            {
                return lhs.m_UID == rhs.m_UID;
            }

            public static bool operator != (CRenderInstance lhs, CRenderInstance rhs)
            {
                return lhs.m_UID != rhs.m_UID;
            }

            public override bool Equals(object obj)
            {
                return this == (CRenderInstance)obj;
            }

            public override int GetHashCode()
            {
                return (int)m_UID;
            }
        }

    }
}
